﻿//using namespace std;
//#include <iostream>
//#include <vector>
//#include <algorithm>
//#include <string>
//#include <map>
////1.auto初始化
//void test01()
//{
//	//使用auto必需立刻初始化
//	//auto s;
//	auto  a = 3;//auto被推导为int
//	auto  b = 6.43;//auto是double
//	auto c = 'c';//auto shi char
//	cout << typeid(a).name() << "," << typeid(b).name() << "," << typeid(c).name() << endl;
//	cout << sizeof(a) << "," << sizeof(b) << "," << sizeof(c) << endl;
//
//	//auto和其他类型混合使用
//	int x = 10;
//	auto* p1 = &x;//p1是int*（指针）类型，auto是int类型
//	cout << "p1类型：" << typeid(p1).name() << endl;
//	auto p2 = &x;//p2是int*类型，auto是int*
//	cout << "p2类型：" << typeid(p2).name() << endl;
//	auto& r1 = x;//r1是int&类型，auto是int类型
//	cout<< "r1类型：" << typeid(r1).name() << endl;
//	auto r2 = r1;//r1是int&，r2应该也是int&类型，但是通过打印我们发现引用类型会打印他的原始类型，
//	cout<< "r2类型：" << typeid(r2).name() << endl;
//}
////1.2 auto的使用限制
//void test()
//{
//	//1.auto不能用于定义数组
//	//auto arr[] = { 1,2,3 };//及时马上初始化也不行
//}
////2) auto不能用于类的成员变量
//class Testauto
//{
//public:
//	//auto m_Age = 20;//初始化也不行
//	int m_Age = 20;
//};
//
////3）auto不能再函数参数中使用
////int add(auto a = 2, auto b = 4) { return a* b; }
//
////4）auto如果作为函数的返回值，可以用于函数的定义，但是不能用于函数的声明。
//auto mySum(int num)
//{
//	int sum = 0;
//	for (int i = 1; i <= num; i++)
//	{
//		sum += i;
//	}
//	return sum;
//}
//
////5）auto不能用于模版参数
//template<class T>
//class A
//{
//	T m_Age;
//};
//A<int> a1;
//A<char> a2;
////A<auto> a2;
//
////1.3 auto的使用场合
//void test02()
//{
//	//1）直接在定义变量的时候使用，
//	//2）在循环中使用auto作为循环控制变量
//	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//	for (auto i = 0; i < 10; i++)
//	{
//		cout << arr[i] << " ";
//	}
//	cout << endl;
//	
//	//3）定义迭代器，使用auto来推导迭代器的类型
//	vector<int> v;
//	v.push_back(1);
//	v.push_back(2);
//	v.push_back(3);
//	//之前的用法
//	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
//	{
//		cout << *it << " ";
//	}
//	cout << endl;
//
//	//使用auto后
//	for (auto it = v.begin(); it != v.end(); it++)
//	{
//		cout << *it<<" ";
//	}
//	cout << endl;
//}
//
////2.空指针nullptr
//void fun(int n)  { cout << n << ",调用参数为int的函数" << endl; }
//void fun(const char* s) { const char* c = "你好"; s = c;  cout << s << "， 调用的是const char*参数的函数" << endl; }
//void test03()
//{
//	fun(NULL);//我们发现NULL当做0处理，调用的是int参数的函数重载，这样显然不是我们期望的。
//	fun(nullptr);//换成nullptr，就不会有这个问题，他会按照我们期望，调用指针参数函数
//	char* p_char = nullptr;
//	int* p_int = nullptr;
//	int n = 10;
//	/*if (n == nullptr){}:注意：nullptr不能跟整型作比较*/
//}
//
////3.Lambda表达式
//void test04()
//{
//	[] {};//这是一个最简单的匿名函数，但是啥也做不了
//	int a = 1;
//	int b = 2;
//	[=] {return a + b; };//值传递捕捉所有变量，返回a+b的和。
//	//执行匿名函数的时候，只需在后面加()
//	cout << "a+b=" << [=] {return a + b; }() << endl;
//	//定义一个带参数的匿名函数
//	[](int num1, int num2) {return num1 + num2; };
//	cout << [](int num1, int num2) {return num1 + num2; }(5, 8) << endl;
//	//定义带返回值的匿名函数
//	cout << [=]()->int {return a + b; }() << endl;//如果有->，不能省略()
//	//结合auto来用
//	auto f = [] {return 100; };
//	cout << "执行匿名函数f：" << f() << endl;
//	//使用引用捕捉
//	auto f1 = [&](int c) {b = a + c; };//修改b的值
//	f1(10);
//	cout << "执行f1之后，a=" << a << ",b =" << b << endl;//b=11
//
//	//mutable的用法：
//	int x = 1;
//	int y = 2;
//	//auto add1 = [x, y]()->int {x *= 2; return x + y; };//报错，因为x具有常性，所以无法修改
//	auto add1 = [x, y]()mutable ->int {x *= 2; return x + y; };//通过mutable取消了捕获变量的常性，
//	cout << add1() << endl;//输出4
//
//	//匿名函数也可以在函数内部调用普通函数
//	auto fun_mysum = []()->int {return mySum(10); };
//	cout << fun_mysum() << endl;
//
//	//课堂练习：使用匿名函数代替STL中的排序器函数，实现排序算法的倒序排序，操作一个vector<int>
//	vector<int> vec{ 3,4,9,6,7 };
//	sort(vec.begin(), vec.end(), [](int a, int b) {return a > b; });
//	for (int num : vec)
//	{
//		cout << num << " ";
//	}
//	cout << endl;
//}
//
////4.for范围遍历
//void test06()
//{
//	//传统遍历方式
//	int arr[6] = { 1,2,3,4,5,6 };
//	for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
//	{
//		cout <<arr[i]<<" ";
//	}
//	cout <<endl;
//
//	char arr_c[] = "hello world";
//	for (int i = 0; i < strlen(arr_c); i++)
//	{
//		cout << arr_c[i];
//	}
//	cout << endl;
//
//	//for范围遍历
//	for (auto i : arr)
//	{
//		cout << i << " ";
//	}
//	cout << endl;
//	for (auto c : arr_c)
//	{
//		cout << c;
//	}
//	cout << endl;
//	//遍历容器
//	vector<int> int_vec = { 1,2,3,4,5 };
//	for (auto i: int_vec) { cout << i << " "; }
//	cout << endl;
//}
//
////通过for循环遍历，实现容器的模版函数，打印vector容器的模版
//template<typename T>
//void printVector(vector<T>& v)
//{
//	for (auto ele : v) { cout << ele << " "; }
//	cout << endl;
//}
//template<typename T1,typename T2>
//void printMap(map<T1, T2>& m)
//{
//	for (auto ele : m) { cout << ele.first << ":" << ele.second << endl; }
//	cout << endl;
//}
//void test07()
//{
//	vector<int> int_vec = { 1,2,3,4,5,6 };
//	vector<string> str_vec = { "hello","world","java","C++" };
//	printVector(int_vec);
//	printVector(str_vec);
//
//	map<int, int> int_map = { { 1,10 }, { 2,20 }, { 3,30 } };
//	map<int, string> int_str_map = { {8483,"张三"},{1994,"李四"},{2949,"王五"} };
//	printMap(int_map);
//	printMap(int_str_map);
//
//	for (auto num : { 24,35,225,335 }) { cout << num << " "; }
//	cout << endl;
//
//	//for范围遍历字符串的时候，会把字符串的结束符也遍历
//	int count = 0;
//	char arr_char[] = "hello";
//	for (int i = 0; i < strlen(arr_char); i++)//strlen不统计结束符\0
//	{
//		count++;
//	}
//	cout << count << endl;//输出5
//	//for范围遍历
//	count = 0;
//	for (auto c : arr_char)
//	{
//		count++;
//	}
//	cout << count << endl;//6
//}
//
////for范围遍历的引用遍历
//void test08()
//{
//	vector<int> int_vec = { 1,2,3,4,5,6 };
//	for (auto& i : int_vec) { cout << ++i << " "; }//引用的方式遍历，每次都+1后再输出
//	cout << endl;
//	//操作完之后，vector中的值就被改变了，输出看一下
//	for (auto i:int_vec ) { cout << i << " "; }
//	cout << endl;
//}
//
////5.初始化列表
////结构体
//struct Point
//{
//	int px;
//	int py;
//	Point (int x,int y):px(x),py(y){}
//};
////类
//class Date
//{
//public:
//	int m_Year;
//	int m_Month;
//	int m_Day;
//	Date (int y,int m,int d):m_Year(y),m_Month(m),m_Day(d){}
//};
//void test09()
//{
//	int arr1[] = { 4,5,6,7,8 };
//	int arr2[5] = { 8 };
//	//自定义类型初始化
//	Point p = { 10,25 };
//	cout << p.px << "," << p.py << endl;
//	Date d1 = { 2024,10,14 };
//	Date d2{ 2024,10,15 };//省略=
//	cout << d1.m_Year << "-" << d1.m_Month << "-" << d1.m_Day << endl;
//	cout << d2.m_Year << "-" << d2.m_Month << "-" << d2.m_Day << endl;
//	//容器初始化
//	vector<int> v_int = { 28,45,25,47,78,64 };
//	vector <Date> v_date = { {2025,1,1},{2025,1,29} };
//	//申请堆内存的时候初始化
//	Point* pp = new Point[3]{ {1,1},{2,2},{3,3} };
//	for (int i = 0; i < 3; i++)
//	{
//		cout << (pp + i)->px << "," << (pp + i)->py << " ";
//	}
//	cout << endl;
//	int* p_int = new int[5] {1, 2, 3, 4, 5};
//	for (int i = 0; i < 5; i++)
//	{
//		cout << *(p_int + i) << " ";
//	}
//	cout <<endl;
//
//	//初始化列表的类型,可见初始化列表使用模版实现，所以可以对不同类型进行初始化
//	auto ls = { 1,2,3 };
//	cout << typeid(ls).name() << endl;
//	auto cls = { 'a','b','c' };
//	cout << typeid(cls).name() << endl;
//}
//
////6.类型别名
//void test10()
//{
//	typedef Date D;//自定义类型起别名
//	typedef double dou;//基本类型起别名
//	using P = Point;//using的语法
//
//	//接下来使用别名
//	D d1 = { 2024,10,15 };
//	dou d = 53.25;
//	P p1 = { 2,4 };
//}
//
////7.右值引用和移动语句
//void test11()
//{
//	int n = 3;
//	int& a = n;//左值引用
//	//int& b = 5;//左值引用无法引用右值
//	int&& b = 5;//右值引用
//	cout << &"hello" << endl;//字符串字面量是左值，可以取地址
//	//cout << &10 << endl;//10为右值，无法取地址
//
//	int n1 = 1;
//	int n2 = 2;
//	//cout << &(n1 + n2) << endl;//表达式计算的结果是临时的，是右值，不能取地址
//	int res = n1 + n2;
//	cout << &res << endl;//res保存了n1+n2的计算结果，他是左值，可以取地址
//}
//
//class Student
//{
//	string name;
//	int age;
//public:
//	Student(string n,int a):name(n),age(a){}
//	void show() { cout << "姓名：" << name << "年龄：" << age << endl; }
//};
//void test12()
//{
//	Student s1("张三", 20);
//	cout << "s1:";
//	s1.show();
//	Student s2(s1);//拷贝构造
//	cout << "s2:";
//	s2.show();
//	Student s3(move(s1));//移动构造，取得对s1的所有权，此时s3就相当于s1，这时候s1就被析构了
//	cout << "s3:" ;
//	s3.show();
//	cout << "s1:";
//	s1.show();
//
//	Student s4 = s2;//赋值函数
//	cout << "s4:";
//	s4.show();
//	Student s5 = move(s2);//move将s2转换为右值，然后使用移动赋值函数，s5相当于原来的s2，s2就被析构了
//	cout << "s5:";
//	s5.show();
//	cout << "s2:";
//	s2.show();
//
//	//在容器中使用移动语义
//	vector<string> vec_str1 = { "helli ","world","welcome","C++" };
//	vector<string> vec_str2 = vec_str1;//这是我们熟悉的方式
//	vector<string> vec_str3 = move(vec_str1);//通过move函数转换右值，实现调用移动赋值函数，提高效率，避免复制的过程。
//
//	vector<vector<int>>res;
//	vector<int>temp;
//	temp.push_back(5);//添加基本类型，没必要移动，因为基本类型无法优化
//	res.push_back(move(temp));//这里使用移动就可以提高效率
//
//}
//int main()
//{
//	test12();
//	return 0;
//}
///*
//C++11新特性
//	1.自动类型推导
//	2.空指针nullptr
//	3.Lambda表达式
//	4.for范围便利
//	5.初始化列表
//	6.类型别名
//	7.右值引用和移动语句
//	8.智能指针
//	9.thread多线程
//
//1.自动类型推导
//	1.1auto初始化
//		在C++11之前，aoto就存在，它用来指定变量的存储类型，它跟static关键字相对的。代表存储，这是编译器默认的规则，所以一般我们都是忽略不写的
//		C++11之后，auto被赋予新的含义，用来做变量类型的自动推导。也就是说，使用auto之后，我们不用指定变量的类型，而是让编译器自动推导变量的类型
//		语法：auto 变量名=变量值；此时auto起到一个占位的作用，编译期间auto会被真正的类型取代。
//		注意事项：使用auto的时候，由于需要根据变量的值来推导具体的类型，所以必须对变量立刻初始化，这样才能推导。
//	1.2 auto的使用限制
//		1）使用auto的时候必须立刻对变量初始化。除此之外，auto不能用于定义数组。
//		2）auto不能用于类的成员变量
//		3）auto不能在函数参数中使用,因为形参只有在函数调用的时候才会被赋值初始化。
//		4）auto如果作为函数的返回值，可以用于函数的定义，但是不能用于函数的声明。
//		5）auto不能用于模版参数
//	1.3 auto的适用场合
//		1）直接在定义变量的时候使用，
//		2）在循环中使用auto作为循环控制变量，比如定义迭代器，使用auto来推导迭代器的类型
//
//2.空指针nullptr
//	空指针NULL大家都不陌生，在c++中除了NULL，0也可以代表空指针。所以NULL会被当做0处理。
//	这种情况，在某些函数调用的时候会存在问题，如果一个函数有重载，重载的是两个不同的参数，一个参数是指针类型，一个参数是int。这种时候，当我们传入
//	NULL的时候，NULL会被解析成0，从而调用int参数的那个函数，而这显然是不对的。
//	为了解决这个问题，引入nullptr来充当空指针。
//	注意：nullptr不能跟整型作比较。
//
//3.Lambda表达式（匿名函数）
//	语法：[捕捉列表] (参数列表) mutable->返回值类型{函数体代码;}
//
//	[捕捉列表]：它一定是出现在匿名函数的最开始位置，不能少，编译器根据[]来判断这是个匿名函数。他的作用是用来捕捉当前作用域中的变量，供匿名函数使用。
//		通过捕捉列表来确定匿名韩式在调用的时候，可以使用那些外部变量。
//		捕捉列表常见用法：
//		[ ]：匿名函数不捕获不使用任何外部变量。（如果是全局变量或者静态变量，匿名函数仍然可以使用）
//		[ 变量a，b，c····]：以传值的方式使用多个外部变量（值捕捉过来的变量具有常性，也就是说不能再匿名函数中修改这些变量）
//		[变量&a，&b，&c···]：以引用的方式使用多个外部变量，（引用捕捉不具有常性，可以修改它们的值）
//		[=]：捕捉所有变量，并且都是值传递
//		[&]：捕捉所有变量，并且都是引用传递
//
//	(参数列表)：它跟普通函数用法相同。如果不传参，可以将()省略不写。但是如果指定了返回值的时候->,()则不能省列。参数是没有常性的，可以修改。
//
//	mutable：默认情况下，值捕捉的变量具有常性，无法修改其值，但是加上mutable之后，就可以取消常型，从而在匿名函数体内部修改它们的值。但是仍然无法修改原值。
//		除非使用引用捕捉才可以修改原值。同时，如果加了mutable，()也不能省略，即使为空。
//
//	->返回值类型：代表匿名函数的返回值类型，如果匿名函数没有返回值，可以省略不写，或者返回值明确的情况下，也可以不写。
//
//	[函数体代码；]：这里面是匿名函数的实现逻辑。这里面可以使用捕捉过来的变量，也可以使用自己的参数和自定义的变量。
//
//4.for范围便利
//	这是一种新的遍历方式，可以对一个可便利的序列进行逐个遍历，但是他无法指定遍历的范围，他只能全部进行遍历。如果要指定范围，任然使用传统的for循环进行遍历。
//	for范围便利语法：for(变量类型 变量名：可遍历的序列){循环体代码；}
//	注意：逐个遍历类型要和序列中元素的类型一致，通过可以直接使用auto来自动推导。可遍历的序列可以是数组、容器，或者一个大括号初始化的序列。
//	for范围遍历引用语法：for（变量类型 变量名：可遍历的序列){循环体代码；}//引用遍历可以修改序列中的原值
//
//5.初始化列表
//	使用{}初始化
//	在C++11之前，就可以使用大括号{}对数组进行初始化
//	C++11之后，扩大了它的适用范围，可以对所有的内置类型和用户自定义类型进行初始化，初始化的时候，可以加=，不可以不加=
//
//6.类型别名
//	类型别名就是给类型（基本类型和自定义类型）起别名，然后可以使用别名，等价于源类型名
//	1）typdef 类型原名 类型别名；
//	2）using 类型别名=类型原名
//	以上两种方式只有一个区别，那就是typedef这种方式不能用于给模板类进行起别名，其他情况，他两是等价的
//
//7.右值引用和移动语句
//	右值引用也是C++11的重要特性，他主要解决性能的问题，可以免除某些场景下的一些额外开销
//	先说下左值和右值：
//		左值：可以放到等号左边的值，可以取地址，并且有名字。比如说函数名、变量名、返回左值引用的函数调用、字符串字面量如"hello"
//		右值：不可哟放到等号左边，不能取地址，也没有名字。比如说运算表达式产生的临时结果，除字符串以外的字面量，非引用返回的临时变量，匿名函数
//		举例：int a=5;//a是左值，可以通过&取地址，而5是个字面量，他没办法取地址，他是右值。
//	右值引用：
//		左值引用是对左值的引用或者叫做别名，右值引用是对右值的引用和别名。语法上：右值引用就是左值引用的基础上多加一个&
//		如：类型 &&右值引用名=右值；
//	为什么要使用右值引用：
//		右值引用主要解决性能问题，c++11之前程序运行过程中会产生大量的临时对象，主要有以下几个方面：
//			函数的返回值
//				最常见的就是返回自定义类型，如果没有用对象接收，就会产生临时对象。比如有个函数返回一个临时对象，但是我们没有使用它
//				这时候编译器会创建一个临时对象来存储这个返回值。而且将来还会调用析构函数将他析构掉，当有大量的临时对象产生的视乎，或者临时对象占用
//				的资源很大的时候，就会对系统性能有较大的影响
//			计算结果
//				一些表达式的计算结果也是临时对象
//			值传递的形参
//				比如有个函数值传递一个对象参数，值传递的过程其实就是做了一个拷贝构造，进行复制。这个时候也会产生临时对象。
//		C++11之后，有了右值引用，就可以解决以上问题，当我们不希望产生临时对象的时候，可以使用移动函数move来将一个对象变成右值，通过
//		移动构造的方式，或者通过移动赋值的方式，来取得这个对象的所有权，从而避免拷贝的过程。提高程序的效率。
//
//		关于移动构造和移动赋值：
//			C++11之前。一个自定类型（结构体或者类）如果我们没有手动声明，编译器会帮我们生成四个函数：
//			构造函数、拷贝构造函数、赋值运算符重载函数、析构函数。
//			C++11之后，编译器会多生辰两个函数：分别是移动构造函数、移动赋值函数。这样我们就可以通过移动的方式，取得对象的所有权，而不必去复制。
//			注意：移动语义针对那些实现了移动构造和移动赋值的自定义类型，对于基本类型没有任何作用的。
//			STL中的容器几乎都是支持移动语义的，这样就可以通过move转换成右值后，调用移动函数进行移动，避免拷贝的动作，提高性能。
//			还有一些特殊的类，它们只允许移动，不允许拷贝。比如unique_ptr、thread。
//
//			右值引用的注意事项：
//				1）和左值引用一样都需要马上初始化
//				2）右值引用无法指向左值，这时候可以使用move函数将它转换成右值。
//				3）对于基本类型，没必要使用move进行移动，因为基本类型的拷贝开销很小。move函数只是将左值转换为右值，但能不能移动，主要看自定义类型中有没有实现移动构造和移动赋值。
//				4）移动之后的对象会被析构，所以通常是对一些临时对象进行移动，或者对不再使用的对象进行移动。如果还想继续使用的对象，就不要移动了。
//				5）使用const修饰的左值引用也可以引用右值，但是无法修改。
//
//*/